package one;

import java.util.*;

public class Bookstore {
    private List<Book> inventory;
    private List<User> users;

    public Bookstore(List<Book> inventory, List<User> users) {
        this.inventory = inventory;
        this.users = users;
    }

    public Bookstore() {
        this.inventory = new ArrayList<>();
        this.users = new ArrayList<>();
    }

    public List<Book> getInventory() {
        return inventory;
    }

    public void setInventory(List<Book> inventory) {
        this.inventory = inventory;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public void addBook(Book book) {
        inventory.add(book);
    }

    public void addUser(User user) {
        users.add(user);
    }

    public List<User> findSimilarUsersByPurchaseHistory(User user) {
        List<User> similarUsers = new ArrayList<>();

        for (User otherUser : users) {
            if (otherUser.getId() != user.getId()) {
                int intersectionSize = 0;
                Set<Book> intersection = new HashSet<>(user.getPurchaseHistory());
                intersection.retainAll(otherUser.getPurchaseHistory());
                intersectionSize = intersection.size();
                if (intersectionSize >= user.getPurchaseHistory().size() / 2) {
                    similarUsers.add(otherUser);
                }
            }
        }

        return similarUsers;
    }

    public List<Book> recommendByPurchaseHistory(User user) {
        List<User> similarUsers = findSimilarUsersByPurchaseHistory(user);
        Set<Book> recommendedBooks = new HashSet<>();

        for (User similarUser : similarUsers) {
            for (Book book : similarUser.getPurchaseHistory()) {
                if (!user.getPurchaseHistory().contains(book)) {
                    recommendedBooks.add(book);
                }
            }
        }

        return new ArrayList<>(recommendedBooks);
    }

    public List<Book> recommendByRatings(User user) {
        List<Book> recommendedBooks = new ArrayList<>();
        Map<Book, Double> scores = new HashMap<>();

        for (Book book : inventory) {
            double score = 0;
            int count = 0;

            for (User otherUser : users) {
                if (otherUser.getRatings().containsKey(book)) {
                    score += otherUser.getRatings().get(book);
                    count++;
                }
            }

            if (count > 0) {
                double averageRating = score / count;
                scores.put(book, averageRating);
            }
        }

        for (Book book : inventory) {
            if (!user.getRatings().containsKey(book)) {
                double score = 0;
                int count = 0;

                for (User otherUser : users) {
                    if (otherUser.getRatings().containsKey(book)) {
                        double similarity = cosineSimilarity(user, otherUser);
                        score += similarity * otherUser.getRatings().get(book);
                        count++;
                    }
                }

                if (count > 0) {
                    double weightedRating = score / count;
                    double predictedRating = 0.5 * weightedRating + 0.5 * scores.get(book);
                    if (predictedRating >= 3.5) {
                        recommendedBooks.add(book);
                    }
                }
            }
        }

        return recommendedBooks;
    }

    private double cosineSimilarity(User user1, User user2) {
        Set<Book> intersection = new HashSet<>(user1.getRatings().keySet());
        intersection.retainAll(user2.getRatings().keySet());

        double dotProduct = 0;
        double norm1 = 0;
        double norm2 = 0;

        for (Book book : intersection) {
            int rating1 = user1.getRatings().get(book);
            int rating2 = user2.getRatings().get(book);

            dotProduct += rating1 * rating2;
            norm1 += rating1 * rating1;
            norm2 += rating2 * rating2;
        }

        if (norm1 == 0 || norm2 == 0) {
            return 0;
        } else {
            return dotProduct / Math.sqrt(norm1 * norm2);
        }
    }
}

/*
1`recommendByPurchaseHistory`方法：
基于用户的购买历史推荐书籍。此算法首先找到和用户购买历史相似的其他用户，
然后将这些用户购买过但是用户本身没有购买过的书籍推荐给用户。

2 recommendByRatings方法：
基于用户对图书评价推荐书籍。此算法首先计算出所有图书的平均评分，
然后对于每个用户没有评价过的图书，根据该用户和其他用户的评分计算出一个加权评分，并结合平均评分进行推荐。

此代码还包含一个私有方法cosineSimilarity，用于计算两个用户之间的余弦相似度，用于在recommendByRatings方法中计算用户之间的相似度。
 */